package com.zeng.incast.videoplay.hardcode;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;


public class AvcDecode {

    private final static String TAG = "AvcDecode";
    private final static String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
    private final static int TIMEOUT_USEC = 10;
    private int m_framerate = 30;//帧率
    private long presentationTimeUs = 0;
    private boolean isSPSAndSSP = false;
    private boolean isFirst = true;
    private boolean isRunnable = false;
    private MediaCodec mediaCodec = null;
    private MediaFormat mediaFormat = null;
    private Surface surface;
    private int spsIndex = -1;
    private int ppsIndex = -1;
    private int idrIndex = -1;
    private byte[] sps = null;
    private byte[] pps = null;
    public ConcurrentLinkedQueue<byte[]> decodeList = new ConcurrentLinkedQueue<byte[]>();

    public AvcDecode(Surface surface) {
        isRunnable = false;
        this.surface = surface;
    }

    public void startDecode() {
        isRunnable = true;
        new Thread(decodeRunnable).start();
    }

    public void stopDecode() {
        isRunnable = false;
    }

    public Runnable decodeRunnable = new Runnable() {
        @Override
        public void run() {
            while (isRunnable) {
                synchronized(decodeList){
                    if (!decodeList.isEmpty()) {
                        byte[] temp = decodeList.poll();
                        if (temp != null && temp.length > 4){
                            decodeH264(temp);
                        }
                    }
                }
            }
            release();
        }
    };

    public void release(){
        if (mediaCodec != null) {
            try {
                mediaCodec.stop();
                mediaCodec.release();
                mediaCodec = null;
            } catch (IllegalStateException e) {
                // TODO 如果当前java状态和jni里面的状态不一致，
                e.printStackTrace();
            }
        }
        if (mediaFormat != null){
            mediaFormat = null;
        }
        if (!decodeList.isEmpty()){
            decodeList.clear();
        }
    }

    public boolean getStatus(){
        return isRunnable;
    }


    public boolean decodeH264(byte[] h264) {
        if (!isSPSAndSSP) {
            for (int i = 0; i < h264.length - 3; i++) {
                if (h264[i] == 0 && h264[i + 1] == 0 && h264[i + 2] == 0 && h264[i + 3] == 1) {
                    int temp = h264[i + 4];
                    String intstr = Integer.toBinaryString(temp);
                    int soulength = intstr.length();
                    for (int index = 0; index < 8 - soulength; index++) {
                        intstr = "0" + intstr;
                    }
                    if (intstr.substring(3).equals("00111")) {
                        spsIndex = i;
                        continue;
                    }
                    if (intstr.substring(3).equals("01000")) {
                        ppsIndex = i;
                        continue;
                    }
                    if (spsIndex != -1 && ppsIndex != -1) {
                        idrIndex = i;
                    }
                    if (spsIndex != -1 && ppsIndex != -1 && idrIndex != -1) {
                        if (spsIndex > ppsIndex || ppsIndex + 1 > idrIndex){
                            spsIndex = ppsIndex = idrIndex = -1;
                            return false;
                        }
                        isSPSAndSSP = true;
                        sps = Arrays.copyOfRange(h264, spsIndex, ppsIndex);
                        pps = Arrays.copyOfRange(h264, ppsIndex + 1, idrIndex);
                        int[] width = new int[1];
                        int[] height = new int[1];

                        width[0] = (H264SPSPaser.ue(sps,34) + 1)*16;
                        height[0] = (H264SPSPaser.ue(sps,-1) + 1)*16;
                        //AvcParse.parseSPS(sps, width, height);
                        mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, 1920, 1080);
                        mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
                        mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
                        mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
                        try {
                            mediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
                            if (surface != null && surface.isValid() && mediaFormat != null){
                                mediaCodec.configure(mediaFormat, surface, null, 0);
                                mediaCodec.start();
                            }else{
                                isSPSAndSSP = false;
                                return false;
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
                    }
                }
            }
        }

        if (isSPSAndSSP) {
            try{
                ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                int inputBufferIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_USEC);//-1表示等待
                //Log.e(TAG, "decodeH264: inputBufferIndex = " + inputBufferIndex);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                    inputBuffer.clear();
                    inputBuffer.put(h264, 0, h264.length);
                    //计算pts
                    long pts = computePresentationTime(presentationTimeUs);
                    mediaCodec.queueInputBuffer(inputBufferIndex, 0, h264.length, pts, 0);
                    presentationTimeUs += 1;
                } else {
                    return false;
                }
                // Get output buffer index
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                while (outputBufferIndex >= 0) {
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return true;
    }

    /**
     * Generates the presentation time for frame N, in microseconds.
     */
    private long computePresentationTime(long frameIndex) {
        return 132 + frameIndex * 1000000 / m_framerate;
    }

}
